

// // 四、类型守卫

// // 4.1 in 关键字
//  ​
//  interface Admin {
//    name: string;
//    privileges: string[];
//  }
//  ​
//  interface Employee {
//    name: string;
//    startDate: Date;
//  }
//  ​
//  type UnknownEmployee = Employee | Admin;
//  ​
//  function printEmployeeInformation(emp: UnknownEmployee) {
//    console.log("Name: " + emp.name);
//    if ("privileges" in emp) {
//      console.log("Privileges: " + emp.privileges);
//    }
//    if ("startDate" in emp) {
//      console.log("Start Date: " + emp.startDate);
//    }
//  }
// // 4.2 typeof 关键字
//  ​
//  function padLeft(value: string, padding: string | number) {
//    if (typeof padding === "number") {
//        return Array(padding + 1).join(" ") + value;
//    }
//    if (typeof padding === "string") {
//        return padding + value;
//    }
//    throw new Error(`Expected string or number, got '${padding}'.`);
//  }
// // typeof 类型保护只支持两种形式：typeof v === "typename" 和 typeof v !== typename，"typename" 必须是 "number"， "string"， "boolean" 或 "symbol"。 但是 TypeScript 并不会阻止你与其它字符串比较，语言不会把那些表达式识别为类型保护。

// // 4.3 instanceof 关键字
//  ​
//  interface Padder {
//    getPaddingString(): string;
//  }
//  ​
//  class SpaceRepeatingPadder implements Padder {
//    constructor(private numSpaces: number) {}
//    getPaddingString() {
//      return Array(this.numSpaces + 1).join(" ");
//    }
//  }
//  ​
//  class StringPadder implements Padder {
//    constructor(private value: string) {}
//    getPaddingString() {
//      return this.value;
//    }
//  }
//  ​
//  let padder: Padder = new SpaceRepeatingPadder(6);
//  ​
//  if (padder instanceof SpaceRepeatingPadder) {
//    // padder的类型收窄为 'SpaceRepeatingPadder'
//  }
// 4.4 自定义类型保护的类型谓词

//  function isNumber(x: any): x is number {
//    return typeof x === "number";
//  }
//  ​
//  function isString(x: any): x is string {
//    return typeof x === "string";
//  }

// let obj={"name":"szq",age:30};
// type type_obj=typeof obj;

// type t=keyof type_obj;

// let res:boolean= "name" in obj;


// type Shape = Circle | Square | Triangle;  

// interface Circle {  
//     kind: "circle";  
//     radius: number;  
// }  

// interface Square {  
//     kind: "square";  
//     sideLength: number;  
// }  

// interface Triangle {  
//     kind: "triangle";  
//     base: number;  
//     height: number;  
// }  

// function isCircle(shape: Shape): shape is Circle {
//     return shape.kind === "circle";  
// }

// // 使用 isCircle 函数来缩小 shape 变量的类型范围
// function calculateArea(shape: Shape) {  
//     if (isCircle(shape)) {  
//         // 在这里，TypeScript 知道 shape 是 Circle 类型  
//         return Math.PI * shape.radius ** 2;  
//     }  
//     // ... 其他形状的面积计算
//     // 注意：这里应该包含对所有其他形状的处理，否则如果传入的 shape 不是 Circle 类型，函数将不会返回任何值。  
//     throw new Error("Unsupported shape type");
// }

// // 定义一个圆的变量  
// const myCircle: Circle = {  
//     kind: "circle",  
//     radius: 5 // 圆的半径为 5  
// };

// // 调用 calculateArea 方法计算圆的面积  
// const area = calculateArea(myCircle);  
// console.log(`The area of the circle is: ${area}`);


abstract class User {
    _name: string;
    constructor(name: string) { this._name = name; }
    // isStudent(): this is Student0 {
    //     return this instanceof Student0;
    // }
    // isStaff(): this is Staff0 {
    //     return this instanceof Staff0;
    // }
    isStudent(): this is Student0 {
        return this instanceof Student0;
    }
    isStaff(): this is Staff0 {
        return this instanceof Staff0;
    }
}

class Student0 extends User {
    study: string; constructor(name: string, study: string) {
        super(name)
        this.study = study
    }
}

class Staff0 extends User {
    workingYears: number; constructor(name: string, workingYears: number) {
        super(name)
        this.workingYears = workingYears
    }
}

function judgeClassType(obj: User) {
    if (obj.isStaff()) {
        // obj的类型被缩小为 Staff
        obj.workingYears;


    } else if (obj.isStudent()) {
        // obj 的类型被缩小为 Student
        // obj.study
    }
}


let student0: Student0 = new Student0("szq", "javascript");
judgeClassType(student0);

let staff0: Staff0 = new Staff0("hlx", 20);
judgeClassType(staff0);
console.log(student0.constructor.name);


type A0 = keyof any;  // string | number | symbol
type U1 = keyof unknown;  // never

type M2<T> = { [P in keyof T]: number };
type M21 = M2<any>;  // { [x: string]: number }
type M22 = M2<unknown>;  // {}

type tt = { [x: string | number | symbol]: number }
//实际上，对于type t={ [P in keyof any]: number };这段代码，
// keyof any返回的是string | number | symbol的联合类型，
// 但在这种情况下，TypeScript 会将联合类型简化为最宽泛的类型，即string类型。
type tt2 = { [P in keyof any]: number };


// type Teacher ={"name":string,"age":number}
// type Teacher2={ [ k in keyof Teacher]:Teacher[k] }

// type Teacher3<T>={ [ k in keyof T]:T[k] }

// let teacher:Teacher3<Teacher>= {name:"szq",age:10} ;

// type t1= typeof teacher;

// type Teacher4={ [ k in keyof typeof teacher]:(typeof teacher)[k] }


let user = { "name": "szq", age: 20 };
type type_user = typeof user;

// function getPropertyValueByName<T extends keyof type_user>
// (key: T, obj:typeof user):type_user[T]
// {return obj[key];}

const getPropertyValueByName: <T extends keyof typeof user>(key: T, obj: typeof user) => (typeof user)[T]
    = (key, obj) => {
        if (key in obj) {
            return obj[key];
        } else {
            return null;
        }
    }

getPropertyValueByName("name", user);
// getPropertyValueByName("sss",user);// 报错

function isValidKey(
    key: string | number | symbol,
    tobject: any
): key is keyof typeof tobject {
    // console.log(key in object);
    return key in tobject;
}

isValidKey("name", user); // true
isValidKey("name1", user);// false
//  type tobj= keyof any




// Pick
// type Pick<T, K extends keyof T> = {
//     [P in K]: T[P];
// };
type type_user_name4 = Pick<type_user, "name">

// Pick解析如下
type type_user_name = { [key in keyof type_user]: type_user[key] };
type type_user_name2 = { [key in keyof { "name": string, "age": number }]: type_user["name"] };

let tteacher = { "name": "szq", "age": 10 };
type type_user_name3 = { [key in keyof typeof tteacher]: type_user["name"] }


// let user = { "name": "szq", age: 20 };
// type type_user = typeof user;
// type type_user = {
//     name: string;
//     age: number;
// }

// Exclude 移除联合的成员 
// type Exclude<T, U> = T extends U ? never : T;

// type Fruit = "apple" | "banana" | "orange"; 
// type Result = Exclude<Fruit, "orange">;  结果是 "apple" | "banana" 
type Result = Exclude<keyof type_user, "name">;

// 对Exclude的理解 下面这段代码的理解是错误的。
type tttt = (keyof type_user) extends "name" ? never : "name"
// 正确的是下面的理解
// 实际的匹配对于 type_user来讲 应该是
// 分两步匹配的结果 在组成联合类型返回。
//  第一步  
type tttt11 = "name" extends "name" ? never : "name"
//  第二步  
type tttt22 = "age" extends "name" ? never : "age"
//  第三步
type Result2 = tttt11 | tttt22

// Omit
// type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type type_user_age = Omit<type_user, "name">
// Omit的解析如下
type type_user_age2 = Pick<type_user, Exclude<keyof type_user, "name">>


type type_U = "a" | "c" | "b";
type type_T = "a" | "c" | "d";
// type type_Exclude = type_T extends type_U ? never : type_T;

type type_Exclude2 = Exclude<type_T, type_U>

//  type Exclude2<T, U> = T extends U ? never : T;

//  type type_Exclude3 =Exclude2<type_T, type_U> 

// type type_U = "a" | "c" | "b";
// type type_T = "a" | "c" | "d";
// type type_Extract =Extract<type_T, type_U> 



// 3. 一些拓展函数
// 下面是一些常用的类型判断函数：

// const isNumber = (val: unknown): val is number => typeof val === 'number'
// const isString = (val: unknown): val is string => typeof val === 'string'
// const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol'
// const isFunction = (val: unknown): val is Function => typeof val === 'function'
// const isObject = (val: unknown): val is Record<any, any> => val !== null && typeof val === 'object'

// function isPromise<T = any>(val: unknown): val is Promise<T> {
//   return isObject(val) && isFunction(val.then) && isFunction(val.catch)
// }

// const objectToString = Object.prototype.toString
// const toTypeString = (value: unknown): string => objectToString.call(value)
// const isPlainObject = (val: unknown): val is object => toTypeString(val) === '[object Object]'
// ————————————————

//                             版权声明：本文为博主原创文章，遵循 CC 4.0 BY-SA 版权协议，转载请附上原文出处链接和本声明。

// 原文链接：https://blog.csdn.net/u014316335/article/details/135768588

const obj = {
    address: "aaaaaaa", phone: 153111111, descri: "bbbbbb",
    "myfun": () => { return 1 }
};
const obj2 = {
    address: "aaaaaaa", phone: 153111111
}


type myobjtype = typeof obj 
type myobj2type = keyof typeof obj2 

//   type Pick<T, K extends keyof T> = {
//     [P in K]: T[P];
// };
type T = typeof obj;
type K = "address" | "descri";
type type_obj = Pick<T, K >;
type type_obj2 = Pick<T, myobj2type >;

type testtype1 = myobjtype["address"];
type testtype2 = myobjtype["phone"];
type testtype3 = myobjtype["descri"];
type testtype4 = myobjtype["myfun"];

type Record1<K extends keyof any, T> = {
    [P in K]: T;
};
let myredord1: Record1<string, string> = { undefined: "key1" };

let a: string = null;
let a1: string = undefined;
let myredord2 = { null: "key1" };

type testypte5 = "a" | "b"
type testtype6 = keyof testypte5;


// type NonNullable<T> = T & {};
type NullableNumber = number | null | undefined;
const nullableNumbers: ReadonlyArray<NullableNumber> = [1, 2, 3, null, undefined];
const numbers: ReadonlyArray<number> = nullableNumbers
    .filter(n => n !== null && n !== undefined);
    console.log(numbers);